#include "demodulator.h"

Demodulator::Demodulator(qlonglong q, qreal energy, QObject *parent) :
  QObject(parent),
  m_q(q),
  m_E(energy),
  m_sqrtQDown(0),
  m_m( log2(q) ),
  signalSet(q)
{
  if (m_m % 2) { // прореженное сигнальное множество
    m_sqrtQDown = qSqrt( m_q / 2.0 );
    m_sqrtQ     = qSqrt( m_q * 2.0 );
    m_l         = qSqrt( (6.0 * m_E) / (2.0*m_q - 1.0) ); // расстояние между сигнальными точками
    m_delta     = m_l * qSqrt(2.0);                       // минимальное расстояние между сигнальными точками
  }
  else {         // цельное сигнальное множество
    m_sqrtQ = qSqrt(m_q);

    // E = ( (d^2) * (q - 1) ) / 6.0
    m_delta = qSqrt( (6.0 * m_E) / (m_q - 1.0) ); // минимальное расстояние между сигнальными точками
    m_l     = m_delta;                            // расстояние между сигнальными точками
  }

  m_A = ( m_l*( m_sqrtQ-1.0 ) ) / 2.0; // амплитуда сигналов QAM

  // задать сигнальное множество
  qlonglong i1, i2;
  if ( m_m % 2 ) { // прореженное сигнальное множество
    // i = i2*sqrtQDown + i1/2;
    for (int i = 0; i < m_q; ++i) {
      i2 = i / m_sqrtQDown;
      i1 = 2 * (i % m_sqrtQDown) + i2 % 2;
//      i1 = 2 * (i % m_sqrtQDown) + (i2 + 1) % 2;

      signalSet[i].setX(  ( m_A * 2.0 * i1 ) / (m_sqrtQ-1.0) - m_A  );
      signalSet[i].setY(  ( m_A * 2.0 * i2 ) / (m_sqrtQ-1.0) - m_A  );
    }
  }
  else {           // цельное сигнальное множество
    for (int i = 0; i < m_q; ++i) {
      i2 = i / m_sqrtQ;
      i1 = i % m_sqrtQ;

      signalSet[i].setX(  ( m_A * 2.0 * i1 ) / (m_sqrtQ-1.0) - m_A  );
      signalSet[i].setY(  ( m_A * 2.0 * i2 ) / (m_sqrtQ-1.0) - m_A  );
    }
  }
}

// демодуляция
QVector<qlonglong> Demodulator::demodulation(const QVector<QPointF> r) {
  QVector<qlonglong> res;

  qlonglong i; // номер сигнала
  for (int k = 0; k < r.size(); ++k) {
    i = match( r.at(k) ); // сопоставить зашумленному сигналу реальный

    res.append(i);
  }

  return res;
}

// расстояние
// постусловие: результат должен быть не отрицательный
inline qreal Demodulator::distance(QPointF point1, QPointF point2) const {
  QPointF tmp = point1 - point2;
  qreal res = ( tmp.x() * tmp.x() ) + ( tmp.y() * tmp.y() );

#ifdef ERRS
  if (res < 0) { // проверка постусловия
    error("Demodulator::distance(): расстояние получилось отрицательным.");
  }
#endif

  return res;
}

// сопоставить зашумленному сигналу реальный
// постусловие: элементы кодового слова не должны превышать значения объема сигнального
//              алфавита
qlonglong Demodulator::match(QPointF si) const {
  qlonglong res;

  if ( m_m % 2 ) { // прореженное сигнальное множество
    // !!!!!!! пока что полный перебор
    res = 0;
    qreal min = distance( signalSet.at(0), si );
    qreal tmp;
    // найти сигнал на минимальном расстоянии от входной точки
    for (qlonglong i = 1; i < signalSet.size(); ++i) {
      tmp = distance( signalSet.at(i), si );
      if (tmp < min) {
        min = tmp;
        res = i;
      }
    }
  }
  else {           // цельное сигнальное множество
    // !!!!!!! пока что полный перебор
    res = 0;
    qreal min = distance( signalSet.at(0), si );
    qreal tmp;
    // найти сигнал на минимальном расстоянии от входной точки
    for (qlonglong i = 1; i < signalSet.size(); ++i) {
      tmp = distance( signalSet.at(i), si );
      if (tmp < min) {
        min = tmp;
        res = i;
      }
    }
  }

#ifdef ERRS
  if ( res >= m_q || res < 0 ) { // проверка постусловия
    error("Demodulator::match(): номер сигнала выходит за пределы сигнального "
          "множества.");
  }
#endif

  return res;
}
